import java.io.*;
import java.util.*;

public class DB implements Serializable{
	
	//Data members
	private Map<String, Record> _map = null;
	private String _owner = null;
	private static final long serialVersionUID = 3149318675491443237L;

	/**
	 * Constructor
	 * @param owner - record of the DB owner peer
	 */
	public DB(Record owner) {
		_owner = owner.get_peerName();
		_map = new TreeMap<String, Record>();
		
		addRecord(owner.get_peerName(), owner); //Add owner peer to DB
	}

	public void setOwnerName(String owner) {
		_owner = owner;
	}
	
	/**
	 * Gets the peers with online status
	 * @return - list of online peers
	 */
	public List<Record> getOnlinePeers() {
		List<Record> result = new LinkedList<Record>();
		
		for (String key : _map.keySet()) {
			Record rec = _map.get(key);
			if (rec.get_status() == 1)
				result.add(rec.clone());
		}
		
		return result;
	}
	
	/**
	 * Updates the version of the DB
	 * @param now
	 */
	private synchronized void updateVersion(long now) {
		_map.get(_owner).set_dbVersion(new Date(now));
		//_map.get(_owner).set_peerVersion(new Date(now));
		_map.get(_owner).set_lastUpdate(new Date(now));
	}
	
	/**
	 * Gets the records which were updated since a specified date
	 * @param from - date of update
	 * @return - list of records
	 */
	public synchronized List<Record> getRecords(Date from) {
		List<Record> retList = new LinkedList<Record>();
		
		for (Map.Entry<String, Record> peer : _map.entrySet()) {
			if (peer.getValue().get_lastUpdate().compareTo(from) == 1)
				retList.add(peer.getValue().clone());
		}
		
		return retList;
	}
	
	/**
	 * Gets the records which were updated since a specified date
	 * @param from - date of update
	 * @param peers - peers to find in DB
	 * @return
	 */
	public synchronized List<Record> getRecords(Date from, List<String> peers) {
		
		List<Record> retList = new LinkedList<Record>();
		
		for (Map.Entry<String, Record> peer : _map.entrySet())
			if (peers.contains(peer.getValue().get_peerName()) && 
				peer.getValue().get_lastUpdate().compareTo(from) == 1)
					retList.add(peer.getValue().clone());
		
		return retList;
	}
	 
	/**
	 * Gets a specific record
	 * @param peerName  - the record name
	 * @return - a record
	 */
	public synchronized Record getRecord(String peerName) {
		
		Record result = _map.get(peerName);
		if (result == null)
			return null;
		
		return _map.get(peerName).clone();
		
	}
	
	/**
	 * Gets the records which updates since a specified date
	 * @param from
	 * @param peerName - specific record to be found
	 * @return - list of records
	 */
	public synchronized List<Record> getRecords(Date from, String peerName) {
		
		List<Record> retList = new LinkedList<Record>();
		List<Record> tempList = new LinkedList<Record>();
		List<Record> values = new LinkedList<Record>(_map.values());
		
		int index = values.indexOf(new Record(peerName));
		if (index < 0)
			return null;
		
		tempList = values.subList(index, values.size());
		
		for (Record record : tempList)
			if (record.get_lastUpdate().compareTo(from) == 1)
				retList.add(record.clone());
		
		return retList;
	}
	
	/**
	 * Updates the last connection time of a record
	 * @param peerName - record key
	 * @param time - new last time connection
	 */
	public synchronized void updateLastConnection(String peerName, Date time) {
		Record record = _map.get(peerName);
		
		if (record == null)
			return;
		
		if (record.get_lastConnectionTime().compareTo(time) != -1)
			return;
		
		record.set_lastConnectionTime(time);
	}
	
	/**
	 * Add a new record to the DB
	 * @param peerName - key of record
	 * @param newPeer - the record to be added
	 */
	public synchronized void addRecord(String peerName, Record newPeer) {
		
		//Get current time
		Date now = Utils.now();
		
		if (!_map.containsKey(peerName)) {
			newPeer.set_lastUpdate(now);
			_map.put(peerName, newPeer);
			
			if (!newPeer.get_peerVersion().equals(Utils.zeroDate()))
				updateVersion(now.getTime());
			return;
		}
		
		Date dbPeerVersion = null;
		Date newPeerVersion = null;
		
		Record record = _map.get(peerName);
		
		if (record == null)
			return;
		
		dbPeerVersion = record.get_peerVersion();
		
		newPeerVersion = newPeer.get_peerVersion();

		//Check if the argument record is newer than the one in DB
		if (newPeerVersion.compareTo(dbPeerVersion) != 1) {
			if (newPeer.get_dbVersion().compareTo(record.get_dbVersion()) == 1)
				record.set_dbVersion(newPeer.get_dbVersion());

			return;
		}
		
		//Remove the current record and add a new one
		_map.remove(peerName); 
		
		newPeer.set_lastUpdate(now);
		_map.put(peerName, newPeer);
		
		//Check if there is a need to update the DB version
		if (!newPeer.get_peerVersion().equals(Utils.zeroDate()))
			updateVersion(now.getTime());
	}


	/**
	 * Gets list of peers that their pictures where updated since the
	 * specified date
	 * @param from - specified date of update
	 * @return - list of records
	 */
	public synchronized List<Record> getPictures(Date from) {
		List<Record> retList = new LinkedList<Record>();
		
		
		for (Map.Entry<String, Record> peer : _map.entrySet()) 
			if (peer.getValue().get_lastUpdate().compareTo(from) == 1)
				retList.add(peer.getValue().clone());
		
		return retList;
	}
	
	/**
	 * Gets the record of a peer that its picture was updated since the specified date
	 * @param from - specified date
	 * @param peerName - key to the DB
	 * @return - a peer record
	 */
	public synchronized List<Record> getPicture(Date from, String peerName) {
		List<Record> retList = new LinkedList<Record>();
		
		Record rec = _map.get(peerName);
		if (rec == null)
			return retList;
		
		if (rec.get_lastUpdate().compareTo(from) == 1)
			retList.add(rec.clone());
				
		return retList;
	}

	/**
	 * Increments the counter of the connections of specified peer
	 * @param peer - specified peer
	 */
	public synchronized void incrementConnectionTries(String peer) {
		Record record = _map.get(peer);
		
		if (record == null || record.get_status() == 0)
			return;
		
		record.inc_tries();
	}
	
	/**
	 * Sets the status of a peer to online
	 * @param peer - specified peer
	 */
	public synchronized void setPeerOnline(String peer) {
		Record record = _map.get(peer);
		
		if (record == null)
			return;
		
		record.setOnline();
	}
	
	/**
	 * Sets the status of a peer to offline
	 * @param peer - specified peer
	 */
	public synchronized void setPeerOffline(String peer) {
		Record record = _map.get(peer);
		
		if (record == null)
			return;
		
		record.setOffline();
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		
		for (String key : _map.keySet()) 
			sb.append(_map.get(key).toString() + "\n");
		
		return sb.toString();
	}
	
	public String get_OwnerName() { return _owner; }

	public synchronized void resetTries() {
		for (String key : _map.keySet()) {
			_map.get(key).set_tries(0);
		}
	}
}
